import numpy as np
import pandas as pd
from my_class import *
import math
import sys
from my_class import Parameters


def get_dist_per(lng1, lat1, lng2, lat2):
    """
    计算地图上任意两点的距离
    :param lng1:
    :param lat1:
    :param lng2:
    :param lat2:
    :return:
    """
    rady1 = math.radians(lat1)
    rady2 = math.radians(lat2)
    a = rady1 - rady2
    b = math.radians(lng1) - math.radians(lng2)
    s = 2 * math.asin(
        math.sqrt(math.sin(a / 2) ** 2 + math.cos(rady1) * math.cos(rady2) * math.sin(b / 2) ** 2)) * 6378.004
    return s


def get_dist(group1, group2):
    """
    计算两个组的距离矩阵
    :param group1:
    :param group2:
    :return:
    """
    dist_mat = np.zeros([len(group1), len(group2)])
    for i in range(len(group1)):
        for j in range(len(group2)):
            # dist_mat[i,j] = ((group1[i][0]-group2[j][0])**2 + (group1[i][1]-group2[j][1])**2)**0.5
            dist_mat[i, j] = get_dist_per(group1[i][0], group1[i][1], group2[j][0], group2[j][1])
    return dist_mat.round(2)


def set_parameters(path, population_num):
    """
    生成并构造参数类，输出
    :param population_num: 种群数量
    :param path:目标文件路径
    :return:
    """
    df0 = pd.read_excel(path, sheet_name="回收仓库")
    df1 = pd.read_excel(path, sheet_name="分拣中心备选点")
    df2 = pd.read_excel(path, sheet_name="拆解工厂")
    df3 = pd.read_excel(path, sheet_name="产品数据")
    df4 = pd.read_excel(path, sheet_name="其他数据")

    t1_lat = df0['纬度'].dropna().tolist()
    t1_lng = df0['经度'].dropna().tolist()
    t1_s_1day = df0.iloc[:, [4, 7, 10, 13]].dropna().values
    t1_recall_cost = df0.iloc[:, [5, 8, 11, 14]].dropna().values
    t1_hold_cost = df0.iloc[:, [6, 9, 12, 15]].dropna().values

    t2_lat = df1['纬度'].dropna().tolist()
    t2_lng = df1['经度'].dropna().tolist()
    t2_cap = df1['最大容量'].dropna().tolist()
    t2_open_cost = df1.iloc[:, 5].dropna().tolist()
    t2_hold_cost = df1.iloc[:, 6:].dropna().values

    t3_lat = df2['纬度'].dropna().tolist()
    t3_lng = df2['经度'].dropna().tolist()

    volume = df3.iloc[0].values[1:]
    weight = df3.iloc[1].values[1:]
    process_cost = df3.iloc[2].values[1:]
    prices = df3.iloc[3].values[1:]

    vv_cost1 = df4.iloc[0, 0]
    vv_cost2 = df4.iloc[0, 1]
    vf_cost1 = df4.iloc[0, 2]
    vf_cost2 = df4.iloc[0, 3]
    period = df4.iloc[0, 4]

    t1_num = len(t1_lat)
    t2_num = len(t2_lat)
    t3_num = len(t3_lat)
    pr = Parameters(period, vf_cost1, vf_cost2, vv_cost1, vv_cost2, volume, weight, process_cost, prices)
    pr.dist12 = get_dist(list(zip(t1_lng, t1_lat)), list(zip(t2_lng, t2_lat)))
    pr.dist23 = get_dist(list(zip(t2_lng, t2_lat)), list(zip(t3_lng, t3_lat)))
    for i in range(t1_num):
        t1 = T1(i, t1_lat[i], t1_lng[i], t1_s_1day[i], t1_recall_cost[i], t1_hold_cost[i])
        pr.t1_list.append(t1)
    for i in range(t2_num):
        t2 = T2(i, t2_lat[i], t2_lat[i], t2_cap[i], t2_open_cost[i], t2_hold_cost[i])
        pr.t2_list.append(t2)
    for i in range(t3_num):
        t3 = T3(i, t3_lat[i], t3_lng[i])
        pr.t3_list.append(t3)
    for t2 in pr.t2_list:
        t2.set_best_t3(pr.dist23, pr.t3_list)
    pr.set_population_num(population_num)
    pr.best = Chromosome(-1, t1_num, t2_num)
    return pr


def get_initial_ppl(pr: Parameters):
    for i in range(pr.ppl_num):
        chrom = Chromosome(0, len(pr.t1_list), len(pr.t2_list))
        chrom.fit(pr)
        pr.ppl.append(chrom)


def update_best(pr: Parameters):
    for chrom in pr.ppl:
        assert isinstance(chrom, Chromosome)
        if chrom.total_revenue > pr.best_revenue:
            pr.best_revenue = chrom.total_revenue
            pr.best.set_sequence(chrom.sequence.copy())


def generate_children(pr: Parameters):
    children_ppl = []
    for i in range(pr.ppl_num):
        indices = [j for j in range(pr.ppl_num) if j != i]
        in1, in2, in3 = random.sample(indices, 3)
        cross = pr.ppl[in1]
        diff1 = pr.ppl[in2]
        diff2 = pr.ppl[in3]
        chrom = pr.ppl[i]
        assert isinstance(chrom, Chromosome)
        child = chrom.diff_crossover(cross, pr.cross_rate, diff1, diff2, pr.best, pr.factor, len(pr.t2_list), pr)
        children_ppl.append(child)
    return children_ppl


def select_ppl(ppl1, ppl2, num):
    """
    锦标赛
    :param num: 染色体长度
    :param ppl1:
    :param ppl2:
    :return:
    """
    new_ppl = []
    seq1 = [i for i in range(num)]
    seq2 = [i for i in range(num)]
    np.random.shuffle(seq1)
    np.random.shuffle(seq2)
    for i in range(num):
        chrom1 = ppl1[seq1[i]]
        chrom2 = ppl2[seq2[i]]
        if chrom1.total_revenue > chrom2.total_revenue:
            new_ppl.append(chrom1)
        else:
            new_ppl.append(chrom2)
    return new_ppl


def mutation(chrom: Chromosome, best: Chromosome, diff1: Chromosome, diff2: Chromosome, factor, t2_num, pr: Parameters):
    for i in range(len(chrom.sequence)):
        chrom.sequence[i] = round(best.sequence[i] + factor * (diff2.sequence[i] - diff1.sequence[i])) % t2_num
        """if random.random() > 0.5:
            chrom.sequence[i] = random.randint(0, t2_num-1)"""
    chrom.clear()
    chrom.fit(pr)


def log_solution(pr: Parameters, file=None):
    if file is not None:
        fl = open(file, "w")
        sys.stdout = fl
    best_sol = pr.best
    assert isinstance(best_sol, Chromosome)
    best_sol.clear()
    best_sol.fit(pr)
    t1_vf_cost, t1_vv_cost, t1_hold_cost, t2_vf_cost, t2_vv_cost, t2_hold_cost, open_cost, recall_cost, t2_process_cost, earning = best_sol.get_total_revenue(pr)
    print("开放的分拣中心的编号：", best_sol.opened_t2)
    print("")
    for i in range(len(best_sol.sequence)):
        print("回收点{0}分配给分拣中心{1}，周期是{2}".format(i, best_sol.sequence[i], best_sol.term_1[i]))
    print("")
    for j in best_sol.opened_t2:
        t2 = pr.t2_list[j]
        assert isinstance(t2, T2)
        print("分拣中心{0}分配给拆解厂{1}，周期是{2}".format(j, t2.best_t3.num, best_sol.term_2[j]))
    print("")
    print("回收点到分拣中心的盘点成本：", t1_vf_cost)
    print("回收点到分拣中心的行驶变动成本：", t1_vv_cost)
    print("回收点的持有成本：", t1_hold_cost)
    print("分拣中心到拆解厂的盘点成本：", t2_vf_cost)
    print("分拣中心到拆解厂的行驶变动成本：", t2_vv_cost)
    print("分拣中心的持有成本：", t2_hold_cost)
    print("分拣中心的开放成本：", open_cost)
    print("回收点的回收成本：", recall_cost)
    print("分拣中心的处理成本：", t2_process_cost)
    print("总收入：", earning)
    print("")
    print("总利润：", best_sol.total_revenue)